# @author:Wei Junjie
# @time:2024/6/11 22:02
# @file request_util.py
# --*--coding: utf-8 --*--
"""
模块说明
"""
import json
import time
from typing import *
import allure
import re
from debug_talk import DebugTalk
import jsonpath
import requests
from common.config_util import read_extract, write_extract, log_info


class RequestUtil:
    def __init__(self):
        self.last_headers = None
        self.base_url = ''
        self.headers = {}

    # 获得session
    session = requests.Session()

    # 统一发送请求
    def send_request(self, method, url, headers=None, **kwargs) -> Optional[requests.Response]:
        """
        :param method: post, get, put, delete...
        :param url: url地址
        :param kwargs: data/file/json/params
        :return: session response
        """
        # 处理基础路径
        self.base_url = self.replace_value_ongoing(self.base_url) + self.replace_value(url)

        log_info.info('请求路径:' + self.base_url)

        # 处理method
        self.last_method = str(method).lower()

        log_info.info('请求方法' + self.last_method)

        # 处理请求头处理
        if headers and isinstance(headers, dict):
            self.last_headers = self.replace_value(headers)

        log_info.info('请求头' + json.dumps(self.last_headers))
        # 请求数据的替换
        for key, value in kwargs.items():
            if key in ['params', 'data', 'file', 'json']:
                value = self.replace_value(value)
                value = self.replace_value_ongoing(value)
                kwargs[key] = value

        log_info.info('请求参数' + json.dumps(kwargs))
        # 发送请求

        req = RequestUtil.session.request(self.last_method, self.base_url, headers=self.last_headers, **kwargs)
        # allure附件
        allure.attach(body=self.last_method, name='请求方式', attachment_type=allure.attachment_type.TEXT)

        allure.attach(body=json.dumps(self.last_headers), name='请求头', attachment_type=allure.attachment_type.TEXT)

        allure.attach(body=json.dumps(kwargs), name='请求参数', attachment_type=allure.attachment_type.TEXT)

        allure.attach(body=req.text, name='响应数据', attachment_type=allure.attachment_type.TEXT)

        #  接口响应不及时,等待两秒
        log_info.info('响应信息{}'.format(req.text))

        time.sleep(3)

        return req

    # 统一替换值的方法
    def replace_value(self, data):
        # 判断值是什么类型
        new_data = ''
        if data and isinstance(data, dict):
            new_data = json.dumps(data, ensure_ascii=False)

        elif isinstance(data, str):
            new_data = data
        # 替换逻辑
        pattern = re.compile(r'\{\{.*?}}')
        replace_list = pattern.findall(new_data)
        # 如果不为空才替换,为空直接跳过
        if replace_list:
            for item in replace_list:
                old_item = item
                new_pattern = re.search(r'\{\{(.*?)}}', old_item).group(1)
                new_item = read_extract(new_pattern)
                new_data = new_data.replace(old_item, new_item)

        if isinstance(data, dict):
            return json.loads(new_data)

        elif isinstance(data, str):
            return new_data

    # 解析yaml文件
    def analysis_yaml(self, caseInfo):
        # 判断一级关键字
        if 'name' in caseInfo and 'valid' in caseInfo and 'request' in caseInfo and 'description' in caseInfo and 'base_url' in caseInfo:
            # 为allure报告增加测试用例标题和测试描述
            allure.dynamic.title(caseInfo['name'])
            allure.dynamic.description(caseInfo['description'])

            if 'method' in caseInfo['request'] and 'url' in caseInfo['request']:
                method = caseInfo['request'].pop('method')
                url = caseInfo['request'].pop('url')
                self.base_url = caseInfo['base_url']
                headers = None
                files = None
                if jsonpath.jsonpath(caseInfo, '$..headers'):
                    headers = caseInfo['request']['headers']
                    del caseInfo['request']['headers']
                if jsonpath.jsonpath(caseInfo, '$..files'):
                    files = caseInfo['request']['files']
                    for key, value in files.items():
                        files[key] = open(value, 'rb')
                    del caseInfo['request']['files']
                # 发送请求
                req = self.send_request(method=method,
                                        url=url,
                                        headers=headers, files=files, **caseInfo['request'])
                return_json = req.json()
                return_text = req.text
                status_code = req.status_code
                expect_result = caseInfo['valid']
                # 有接口关联的情况
                if 'extract' in caseInfo.keys():
                    for key, value in caseInfo['extract'].items():
                        # 说明是正则提取
                        if '(.+?)' in value or '(.*?)' in value:
                            text_data = re.search(value, return_text)
                            if text_data:
                                write_extract({key: text_data.group(1)})
                        # 说明是json提取
                        else:
                            json_data = return_json[value]
                            write_extract({key: json_data})
                self.valid_result(expect_result, return_json, status_code)
            else:
                log_info.error('在request的一级关键字下必须包含method和url两个二级关键字')
        else:
            log_info.error('缺少必要的关键字段请核查')

    # 动态加载函数
    def replace_value_ongoing(self, data):
        # 判断值是什么类型
        new_data = " "
        if data and isinstance(data, dict):
            new_data += json.dumps(data)
            #  json替换逻辑
            pattern = re.compile(r'"\$\{.*?}"')
            replace_list = pattern.findall(new_data)

            # 如果不为空才替换,为空直接跳过
            if replace_list:
                for item in replace_list:
                    old_item = item
                    # 完整的函数
                    function = re.search(r'"\$\{(.*?)}"', old_item).group(1)

                    # 函数值->为str,如果需要整数则可在debug函数中强制转换

                    args = re.search(r'\((.*)\)', function).group(1)

                    function_name = re.search(r'(.*?)\(', function).group(1)
                    # 判断数据是否是集合
                    if '{' and '(' and '[' not in args:
                        args = args.split(',')
                        # 不是集合的情况
                        new_item = getattr(DebugTalk(), function_name)(*tuple(args))
                        # 返回值判断
                        if isinstance(new_item, int):

                            new_data = new_data.replace(old_item, str(new_item))
                        else:
                            new_data = new_data.replace(old_item, '\"' + new_item + '\"')
                    else:
                        # 是集合的情况
                        new_item = getattr(DebugTalk(), function_name)(args)
                        # 返回值判断
                        if isinstance(new_item, int):

                            new_data = new_data.replace(old_item, str(new_item))
                        else:
                            new_data = new_data.replace(old_item, '\"' + new_item + '\"')

                return json.loads(new_data)

        elif isinstance(data, str):
            new_data += data

            pattern = re.compile(r'\$\{.*?}')

            replace_list = pattern.findall(new_data)

            for item in replace_list:
                old_item = item

                function = re.search(r'\$\{(.*?)}', old_item).group(1)

                function_name = re.search(r'(.*?)\(', function).group(1)

                args = re.search(r'\((.*)\)', function).group(1)
                if '{' and '(' and '[' not in args:
                    args = args.split(',')
                    # 不是集合的情况
                    new_item = getattr(DebugTalk(), function_name)(*tuple(args))
                    # 返回值判断
                    if isinstance(new_item, int):
                        new_data = new_data.replace(old_item, str(new_item))
                    elif isinstance(new_item, str):
                        new_data = new_data.replace(old_item, new_item)
                    else:
                        new_data = new_data.replace(old_item, '\"' + new_item + '\"')
                else:
                    # 是集合的情况
                    new_item = getattr(DebugTalk(), function_name)(args)
                    # 返回值判断
                    if isinstance(new_item, int):
                        new_data = new_data.replace(old_item, str(new_item))
                    elif isinstance(new_item, str):
                        new_data = new_data.replace(old_item, new_item)
                    else:
                        new_data = new_data.replace(old_item, '\"' + new_item + '\"')

            return new_data

    # 断言函数
    def valid_result(self, expect_result, actually_result, status_code):
        flag = 0
        # 期望值为列表时
        if expect_result and isinstance(expect_result, list):
            # 搜索所有的断言
            # 定义一个flag用于判断是否断言成功初始值为0,如果失败则加1
            for item in expect_result:
                # 对每个断言进行判断
                for key, value in item.items():
                    if key == "equal":
                        for assert_key, assert_value in value.items():
                            if assert_key == "status_code":
                                if status_code != assert_value:
                                    flag += 1
                                    log_info.error("断言失败,{}!={}".format(status_code, assert_value))
                            else:
                                actually_result = jsonpath.jsonpath(actually_result, '$..{}'.format(assert_key))
                                if actually_result:
                                    if assert_value not in actually_result:
                                        flag += 1
                                        log_info.error('断言失败:{}'.format(assert_value) + "不在返回结果中")
                                else:
                                    flag += 1
                                    log_info.error('断言失败:返回结果中不存在{}'.format(assert_key))

                    elif key == "contain":
                        if value not in json.dumps(actually_result, ensure_ascii=False):
                            flag += 1
                            log_info.error('断言失败:返回结果中不存在{}'.format(value))

                    else:
                        log_info.error('暂不支持其他断言方式')
        assert flag == 0


if __name__ == '__main__':
    pass
